import Dep from "./dep.js"



const ArrayProto =  Array.prototype;
// 拦截后的Proto
const NewArray = Object.create(ArrayProto);

// 对数组的方法进行拦截
void [
    "push",
    "pop",
    "shift",
    "unshift",
    "splice",
    "sort",
    "reverse",    
].forEach(function(method) {
    const f = ArrayProto[method];
    Object.defineProperty(NewArray,method,{
        value: function(...args) {
            // 监听相关
            const ob = this.__ob__;
            let inserted;
            switch(method){
                case "push":
                case "shift":
                    inserted = args;
                    break;
                case "splice":
                    inserted = args.splice(2)
                    break
            }
            const result = f.apply(this,args);
            if(inserted) ob.observeArray(inserted);
            ob.dep.notify(this);
            return result;
        },
        enumerable: false,
        writable: true,
        configurable: true
    })
});

const hasProto = '__proto__' in {};
const protoAugument = function(data,newArray) {
    data.__proto__ = newArray;
}

const copyAugument = function(data,newArray) {
    const ArrayKeys = Object.getOwnPropertyNames(NewArray);
    for(let x of ArrayKeys) {
        data[x] = newArray[x];
    }
}

const def = function(data,key,val,enumerable) {
    Object.defineProperty(data,key,{
        value:val,
        enumerable: !!enumerable,
        configurable: true,
        writable: true   
    })
}

export default class Observer {
    constructor(data) {
        // 挂载一个dep方便---数组方法进行访问
        this.dep = new Dep();
        // 将Observer实例挂载到data上
        def(data,"__ob__",this);

        if(Array.isArray(data)) {
            // 处理数组相关
            const augument = hasProto?
                protoAugument:
                copyAugument;
            augument(data,NewArray);    
            this.observeArray(data);                                                    
        } else {
            this.walk(data);
        }
    }
    walk(data) {
        for(let key in data) {
            activate(data,key,data[key]);
        }
    }
    observeArray(arr) {
        for(let i=0;i<arr.length;i++) {
            observer(arr[i]);
        }
    }
}
const observer = function(data) {
    if(typeof data !== "object") {
        return 
    }
    if(data.__ob__ && data.__ob__ instanceof Observer) {
        return data.__ob__;
    } else {
        new Observer(data);
        return data.__ob__;
    }
}


// 将data改造为侦听属性并且递归改造
const activate = function(data,key,value) {
    // 如果以及是Observer则返回Observer实例,否则进行Observer(非对象不用这个操作)
    let ob = observer(value);
    const dep = new Dep();
    Object.defineProperty(data,key,{
        enumerable: true,
        configurable: true,
        get() {
            dep.add();
            
            // 同时也添加到observer的dep中--方便数组方法访问
            if(ob) {
                ob.dep.add();
            }
            return value 
        },
        set(val) {
            dep.notify(val,value);
            value = val;
        }     
    });
}